<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="CCULh" id="CCULh"><span data-lake-id="ue36d1a6d" id="ue36d1a6d">典型回答</span></h1>
  <p data-lake-id="u1e5017b7" id="u1e5017b7"><br></p>
  <p data-lake-id="u31f9dc72" id="u31f9dc72"><span data-lake-id="u90a53172" id="u90a53172">JDK 8中推出了Lambda表达式、Stream、Optional、新的日期API等</span></p>
  <p data-lake-id="u662f4a69" id="u662f4a69"><span data-lake-id="u11f7de7f" id="u11f7de7f">JDK 9中推出了模块化</span></p>
  <p data-lake-id="uaed3e8b4" id="uaed3e8b4"><span data-lake-id="u42bc026a" id="u42bc026a">JDK 10中推出了本地变量类型推断</span></p>
  <p data-lake-id="uf842828f" id="uf842828f"><span data-lake-id="ua12e854b" id="ua12e854b">JDK 12中增加了switch表达式</span></p>
  <p data-lake-id="u9bb54b2f" id="u9bb54b2f"><span data-lake-id="u2c89e658" id="u2c89e658">JDK 13中增加了text block</span></p>
  <p data-lake-id="u7950eab4" id="u7950eab4"><span data-lake-id="ud37461f8" id="ud37461f8">JDK 14中增加了Records</span></p>
  <p data-lake-id="u9fcb3004" id="u9fcb3004"><span data-lake-id="u5e2ea3e0" id="u5e2ea3e0">JDK 14中增加了instance模式匹配</span></p>
  <p data-lake-id="u3e937389" id="u3e937389"><span data-lake-id="uc2d79dbb" id="uc2d79dbb">JDK 15中增加了封闭类</span></p>
  <p data-lake-id="uad4511b7" id="uad4511b7"><span data-lake-id="u7fde8a04" id="u7fde8a04">JDK 17中扩展了switch模式匹配</span></p>
  <p data-lake-id="u5f53a3ec" id="u5f53a3ec"><span data-lake-id="u77fb8489" id="u77fb8489">JDK 19中增加了协程</span></p>
  <p data-lake-id="ucec17d18" id="ucec17d18"><span data-lake-id="u211c3f86" id="u211c3f86">​</span><br></p>
  <h1 data-lake-id="m5G6C" id="m5G6C"><span data-lake-id="ucacaa931" id="ucacaa931">扩展知识</span></h1>
  <h2 data-lake-id="q8Sxb" id="q8Sxb"><span data-lake-id="uc85bdf00" id="uc85bdf00">本地变量类型推断</span></h2>
  <p data-lake-id="uf79d0a69" id="uf79d0a69"><br></p>
  <p data-lake-id="u5ca6fc3a" id="u5ca6fc3a"><span data-lake-id="u334d25af" id="u334d25af">在Java 10之前版本中，我们想定义定义局部变量时。我们需要在赋值的左侧提供显式类型，并在赋值的右边提供实现类型：</span></p>
  <p data-lake-id="u32529575" id="u32529575"><br></p>
  <pre lang="java"><code>
MyObject value = new MyObject();
</code></pre>
  <p data-lake-id="u28bc721c" id="u28bc721c"><br></p>
  <p data-lake-id="u6d49597e" id="u6d49597e"><span data-lake-id="ufd990c70" id="ufd990c70">在Java 10中，提供了本地变量类型推断的功能，可以通过var声明变量：</span></p>
  <p data-lake-id="u3ef7483e" id="u3ef7483e"><br></p>
  <pre lang="java"><code>
var value = new MyObject();
</code></pre>
  <p data-lake-id="u6532cb6d" id="u6532cb6d"><br></p>
  <p data-lake-id="u6fbc13c6" id="u6fbc13c6"><span data-lake-id="u2cb6c5a8" id="u2cb6c5a8">本地变量类型推断将引入“var”关键字，而不需要显式的规范变量的类型。</span></p>
  <p data-lake-id="uf276e0cf" id="uf276e0cf"><br></p>
  <p data-lake-id="u07a3ca5e" id="u07a3ca5e"><span data-lake-id="ud925575c" id="ud925575c">其实，所谓的本地变量类型推断，也是Java 10提供给开发者的语法糖。</span></p>
  <p data-lake-id="uf9470d20" id="uf9470d20"><br></p>
  <p data-lake-id="u70746385" id="u70746385"><span data-lake-id="u6a0360c9" id="u6a0360c9">虽然我们在代码中使用var进行了定义，但是对于虚拟机来说他是不认识这个var的，在java文件编译成class文件的过程中，会进行解糖，使用变量真正的类型来替代var</span></p>
  <p data-lake-id="u1ca216d8" id="u1ca216d8"><br></p>
  <h2 data-lake-id="A3JpI" id="A3JpI"><span data-lake-id="u607aea19" id="u607aea19">Switch 表达式</span></h2>
  <p data-lake-id="u98d117b9" id="u98d117b9"><br></p>
  <p data-lake-id="u9c6b9a5b" id="u9c6b9a5b"><span data-lake-id="uab6f4ff8" id="uab6f4ff8">在JDK 12中引入了Switch表达式作为预览特性。并在Java 13中修改了这个特性，引入了yield语句，用于返回值。</span></p>
  <p data-lake-id="ubfc8ec6d" id="ubfc8ec6d"><br></p>
  <p data-lake-id="u88d80b8f" id="u88d80b8f"><span data-lake-id="uc711723f" id="uc711723f">而在之后的Java 14中，这一功能正式作为标准功能提供出来。</span></p>
  <p data-lake-id="u1cf39267" id="u1cf39267"><br></p>
  <p data-lake-id="uedee824c" id="uedee824c"><span data-lake-id="u0b5f4ac3" id="u0b5f4ac3">在以前，我们想要在switch中返回内容，还是比较麻烦的，一般语法如下：</span></p>
  <p data-lake-id="ud637e39e" id="ud637e39e"><br></p>
  <pre lang="java"><code>
int i;
switch (x) {
    case "1":
        i=1;
        break;
    case "2":
        i=2;
        break;
    default:
        i = x.length();
        break;
}
</code></pre>
  <p data-lake-id="u02adea1f" id="u02adea1f"><br></p>
  <p data-lake-id="u375d53ea" id="u375d53ea"><span data-lake-id="u3680502a" id="u3680502a">在JDK13中使用以下语法：</span></p>
  <p data-lake-id="ueb9cb979" id="ueb9cb979"><br></p>
  <pre lang="java"><code>
int i = switch (x) {
    case "1" -&gt; 1;
    case "2" -&gt; 2;
    default -&gt; {
        int len = args[1].length();
        yield len;
    }
};
</code></pre>
  <p data-lake-id="uaf44f727" id="uaf44f727"><br></p>
  <p data-lake-id="u58609f38" id="u58609f38"><span data-lake-id="u5a895a42" id="u5a895a42">或者</span></p>
  <p data-lake-id="uc532ad6f" id="uc532ad6f"><br></p>
  <pre lang="java"><code>
int i = switch (x) {
    case "1": yield 1;
    case "2": yield 2;
    default: {
        int len = args[1].length();
        yield len;
    }
};
</code></pre>
  <p data-lake-id="u6b68f2b6" id="u6b68f2b6"><br></p>
  <p data-lake-id="u8376f71c" id="u8376f71c"><span data-lake-id="uf9239d35" id="uf9239d35">在这之后，switch中就多了一个关键字用于跳出switch块了，那就是yield，他用于返回一个值。</span></p>
  <p data-lake-id="uaafed280" id="uaafed280"><br></p>
  <p data-lake-id="u66ea39b9" id="u66ea39b9"><span data-lake-id="uf1325d06" id="uf1325d06">和return的区别在于：return会直接跳出当前循环或者方法，而yield只会跳出当前switch块。</span></p>
  <p data-lake-id="u4ab3b6a4" id="u4ab3b6a4"><br></p>
  <h2 data-lake-id="m8uSQ" id="m8uSQ"><span data-lake-id="u0021a481" id="u0021a481">Text Blocks</span></h2>
  <p data-lake-id="u2fc40704" id="u2fc40704"><br></p>
  <p data-lake-id="ua9f72c65" id="ua9f72c65"><span data-lake-id="u0e0a9a41" id="u0e0a9a41">Java 13中提供了一个Text Blocks的预览特性，并且在Java 14中提供了第二个版本的预览。</span></p>
  <p data-lake-id="u705afaec" id="u705afaec"><br></p>
  <p data-lake-id="ue5e42007" id="ue5e42007"><span data-lake-id="u3be037c1" id="u3be037c1">text block，文本块，是一个多行字符串文字，它避免了对大多数转义序列的需要，以可预测的方式自动格式化字符串，并在需要时让开发人员控制格式。</span></p>
  <p data-lake-id="u6fccc777" id="u6fccc777"><br></p>
  <p data-lake-id="ue400adb0" id="ue400adb0"><span data-lake-id="u202337ff" id="u202337ff">我们以前从外部copy一段文本串到Java中，会被自动转义，如有一段以下字符串：</span></p>
  <p data-lake-id="ufe748dd6" id="ufe748dd6"><br></p>
  <pre lang="java"><code>
 &lt;html&gt;
  &lt;body&gt;
      &lt;p&gt;Hello, world&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
  <p data-lake-id="u8f6eeb77" id="u8f6eeb77"><br></p>
  <p data-lake-id="u5a88e37b" id="u5a88e37b"><span data-lake-id="u8d0bb5c8" id="u8d0bb5c8">将其复制到Java的字符串中，会展示成以下内容：</span></p>
  <p data-lake-id="u6ba83042" id="u6ba83042"><br></p>
  <pre lang="java"><code>
"&lt;html&gt;\n" +
"    &lt;body&gt;\n" +
"        &lt;p&gt;Hello, world&lt;/p&gt;\n" +
"    &lt;/body&gt;\n" +
"&lt;/html&gt;\n";
</code></pre>
  <p data-lake-id="u55fd8a6f" id="u55fd8a6f"><br></p>
  <p data-lake-id="uac222b74" id="uac222b74"><span data-lake-id="ucc4b174b" id="ucc4b174b">即被自动进行了转义，这样的字符串看起来不是很直观，在JDK 13中，就可以使用以下语法了：</span></p>
  <p data-lake-id="u458ce509" id="u458ce509"><br></p>
  <pre lang="java"><code>
"""
&lt;html&gt;
  &lt;body&gt;
      &lt;p&gt;Hello, world&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;
""";
</code></pre>
  <p data-lake-id="ua8f826aa" id="ua8f826aa"><br></p>
  <p data-lake-id="ufb372884" id="ufb372884"><span data-lake-id="u6a474d3d" id="u6a474d3d">使用</span><code data-lake-id="u8cbeaad2" id="u8cbeaad2"><span data-lake-id="u9bd981ad" id="u9bd981ad">"""</span></code><span data-lake-id="u7aa3e0bd" id="u7aa3e0bd">作为文本块的开始符合结束符，在其中就可以放置多行的字符串，不需要进行任何转义。看起来就十分清爽了。</span></p>
  <p data-lake-id="u4d6ee7e0" id="u4d6ee7e0"><br></p>
  <p data-lake-id="u2a710584" id="u2a710584"><span data-lake-id="u0c09c2e4" id="u0c09c2e4">如常见的SQL语句：</span></p>
  <p data-lake-id="u42e85758" id="u42e85758"><br></p>
  <pre lang="java"><code>
String query = """
    SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
    WHERE `CITY` = 'INDIANAPOLIS'
    ORDER BY `EMP_ID`, `LAST_NAME`;
""";
</code></pre>
  <p data-lake-id="u3f8956a3" id="u3f8956a3"><br></p>
  <p data-lake-id="u760ddc41" id="u760ddc41"><span data-lake-id="u8c7a27e3" id="u8c7a27e3">看起来就比较直观，清爽了。</span></p>
  <p data-lake-id="u59c608d4" id="u59c608d4"><br></p>
  <h2 data-lake-id="oR2v0" id="oR2v0"><span data-lake-id="u9ed38a2d" id="u9ed38a2d">Records</span></h2>
  <p data-lake-id="u5dd0d91d" id="u5dd0d91d"><br></p>
  <p data-lake-id="u2927918a" id="u2927918a"><span data-lake-id="u063123d3" id="u063123d3">Java 14 中便包含了一个新特性：EP 359: Records，</span></p>
  <p data-lake-id="u3e056444" id="u3e056444"><br></p>
  <p data-lake-id="u9aebe7df" id="u9aebe7df"><span data-lake-id="u0e315185" id="u0e315185">Records的目标是扩展Java语言语法，Records为声明类提供了一种紧凑的语法，用于创建一种类中是“字段，只是字段，除了字段什么都没有”的类。</span></p>
  <p data-lake-id="ue8c4a25a" id="ue8c4a25a"><br></p>
  <p data-lake-id="u226b94b1" id="u226b94b1"><span data-lake-id="u8db08920" id="u8db08920">通过对类做这样的声明，编译器可以通过自动创建所有方法并让所有字段参与hashCode()等方法。这是JDK 14中的一个预览特性。</span></p>
  <p data-lake-id="ue6d20cc3" id="ue6d20cc3"><br></p>
  <p data-lake-id="ue5258db7" id="ue5258db7"><span data-lake-id="u5032a8da" id="u5032a8da">使用record关键字可以定义一个记录：</span></p>
  <p data-lake-id="ub1e8539f" id="ub1e8539f"><br></p>
  <pre lang="java"><code>
record Person (String firstName, String lastName) {}
</code></pre>
  <p data-lake-id="u51b570cf" id="u51b570cf"><br></p>
  <p data-lake-id="u80cf0df6" id="u80cf0df6"><span data-lake-id="u9174db8b" id="u9174db8b">record 解决了使用类作为数据包装器的一个常见问题。纯数据类从几行代码显著地简化为一行代码。（详见：</span><a href="http://www.hollischuang.com/archives/4548" target="_blank" data-lake-id="ucd82cbfb" id="ucd82cbfb"><span data-lake-id="ub1320a51" id="ub1320a51">Java 14 发布了，不使用”class”也能定义类了？还顺手要干掉Lombok！</span></a><span data-lake-id="u75e61e26" id="u75e61e26">）</span></p>
  <p data-lake-id="ucf6e5d4e" id="ucf6e5d4e"><br></p>
  <h2 data-lake-id="NP5ww" id="NP5ww"><span data-lake-id="u28475e23" id="u28475e23">封闭类</span></h2>
  <p data-lake-id="u08bdceb6" id="u08bdceb6"><br></p>
  <p data-lake-id="u9d0ba962" id="u9d0ba962"><span data-lake-id="u44a29cf0" id="u44a29cf0">在Java 15之前，Java认为"代码重用"始终是一个终极目标，所以，一个类和接口都可以被任意的类实现或继承。</span></p>
  <p data-lake-id="ue7e18def" id="ue7e18def"><br></p>
  <p data-lake-id="ubb2532a2" id="ubb2532a2"><span data-lake-id="uc162c65b" id="uc162c65b">但是，在很多场景中，这样做是容易造成错误的，而且也不符合物理世界的真实规律。</span></p>
  <p data-lake-id="u9419e4d2" id="u9419e4d2"><br></p>
  <p data-lake-id="ub7837aef" id="ub7837aef"><span data-lake-id="ua614bd99" id="ua614bd99">例如，假设一个业务领域只适用于汽车和卡车，而不适用于摩托车。</span></p>
  <p data-lake-id="ue98269ba" id="ue98269ba"><br></p>
  <p data-lake-id="u1186e464" id="u1186e464"><span data-lake-id="udeae27a7" id="udeae27a7">在Java中创建Vehicle抽象类时，应该只允许Car和Truck类扩展它。</span></p>
  <p data-lake-id="u1f826332" id="u1f826332"><br></p>
  <p data-lake-id="uc52f9195" id="uc52f9195"><span data-lake-id="u0cd7cc4d" id="u0cd7cc4d">通过这种方式，我们希望确保在域内不会出现误用Vehicle抽象类的情况。</span></p>
  <p data-lake-id="uc571141f" id="uc571141f"><br></p>
  <p data-lake-id="u0554953b" id="u0554953b"><span data-lake-id="u873c2f4b" id="u873c2f4b">为了解决类似的问题，在Java 15中引入了一个新的特性——密闭。</span></p>
  <p data-lake-id="ubab3cb45" id="ubab3cb45"><br></p>
  <p data-lake-id="uce075733" id="uce075733"><span data-lake-id="ufe1c255a" id="ufe1c255a">想要定义一个密闭接口，可以将sealed修饰符应用到接口的声明中。然后，permit子句指定允许实现密闭接口的类：</span></p>
  <p data-lake-id="u9b8b00ff" id="u9b8b00ff"><br></p>
  <pre lang="java"><code>
public sealed interface Service permits Car, Truck {
}
</code></pre>
  <p data-lake-id="uf382919d" id="uf382919d"><br></p>
  <p data-lake-id="u0f5e3e61" id="u0f5e3e61"><span data-lake-id="udeb46312" id="udeb46312">以上代码定义了一个密闭接口Service，它规定只能被Car和Truck两个类实现。</span></p>
  <p data-lake-id="ua666b7be" id="ua666b7be"><br></p>
  <p data-lake-id="u6b3ab91e" id="u6b3ab91e"><span data-lake-id="u44389aba" id="u44389aba">与接口类似，我们可以通过使用相同的sealed修饰符来定义密闭类：</span></p>
  <p data-lake-id="uce08cc95" id="uce08cc95"><br></p>
  <pre lang="java"><code>
public abstract sealed class Vehicle permits Car, Truck {
}
</code></pre>
  <p data-lake-id="u60e4e747" id="u60e4e747"><br></p>
  <p data-lake-id="ue14983b7" id="ue14983b7"><span data-lake-id="u92699f3b" id="u92699f3b">通过密闭特性，我们定义出来的Vehicle类只能被Car和Truck继承。</span></p>
  <p data-lake-id="u1f0bf5e5" id="u1f0bf5e5"><br></p>
  <h2 data-lake-id="txhAQ" id="txhAQ"><span data-lake-id="uf6580088" id="uf6580088">instanceof 模式匹配</span></h2>
  <p data-lake-id="u5a2273be" id="u5a2273be"><br></p>
  <p data-lake-id="u49b9ec76" id="u49b9ec76"><span data-lake-id="u39737e1a" id="u39737e1a">instanceof是Java中的一个关键字，我们在对类型做强制转换之前，会使用instanceof做一次判断，例如：</span></p>
  <p data-lake-id="u711c047a" id="u711c047a"><br></p>
  <pre lang="java"><code>
if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    cat.miaow();
} else if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.bark();
}
</code></pre>
  <p data-lake-id="u7b237758" id="u7b237758"><br></p>
  <p data-lake-id="u4fdb1e38" id="u4fdb1e38"><span data-lake-id="u3eeafde4" id="u3eeafde4">Java 14带来了改进版的instanceof操作符，这意味着我们可以用更简洁的方式写出之前的代码例子：</span></p>
  <p data-lake-id="uc8d364df" id="uc8d364df"><br></p>
  <pre lang="java"><code>
if (animal instanceof Cat cat) {
    cat.miaow();
} else if(animal instanceof Dog dog) {
    dog.bark();
}
</code></pre>
  <p data-lake-id="ua0d05fc2" id="ua0d05fc2"><br></p>
  <p data-lake-id="u1666de32" id="u1666de32"><span data-lake-id="ud35cf6e9" id="ud35cf6e9">我们都不难发现这种写法大大简化了代码，省略了显式强制类型转换的过程，可读性也大大提高了。</span></p>
  <p data-lake-id="u9f4f4f18" id="u9f4f4f18"><br></p>
  <h2 data-lake-id="CcjKW" id="CcjKW"><span data-lake-id="ue6021969" id="ue6021969">switch 模式匹配</span></h2>
  <p data-lake-id="u62d98aea" id="u62d98aea"><br></p>
  <p data-lake-id="uea5c8166" id="uea5c8166"><span data-lake-id="u9fffde52" id="u9fffde52">基于instanceof模式匹配这个特性，我们可以使用如下方式来对对象o进行处理：</span></p>
  <p data-lake-id="ud5091fa2" id="ud5091fa2"><br></p>
  <pre lang="java"><code>
static String formatter(Object o) {
    String formatted = "unknown";
    if (o instanceof Integer i) {
        formatted = String.format("int %d", i);
    } else if (o instanceof Long l) {
        formatted = String.format("long %d", l);
    } else if (o instanceof Double d) {
        formatted = String.format("double %f", d);
    } else if (o instanceof String s) {
        formatted = String.format("String %s", s);
    }
    return formatted;
}
</code></pre>
  <p data-lake-id="u6603d1a9" id="u6603d1a9"><br></p>
  <p data-lake-id="u11c1acf1" id="u11c1acf1"><span data-lake-id="ue0c44f68" id="ue0c44f68">可以看到，这里使用了很多if-else，其实，Java中给我们提供了一个多路比较的工具，那就是switch，而且从Java 14开始支持switch表达式，但switch的功能一直都是非常有限的。</span></p>
  <p data-lake-id="ud9aa84c7" id="ud9aa84c7"><br></p>
  <p data-lake-id="ua4452491" id="ua4452491"><span data-lake-id="u391225b7" id="u391225b7">在Java 17中，Java的工程师们扩展了switch语句和表达式，使其可以适用于任何类型，并允许case标签中不仅带有变量，还能带有模式匹配。我们就可以更清楚、更可靠地重写上述代码，例如：</span></p>
  <p data-lake-id="uca03dc62" id="uca03dc62"><br></p>
  <pre lang="java"><code>
static String formatterPatternSwitch(Object o) {
    return switch (o) {
        case Integer i -&gt; String.format("int %d", i);
        case Long l    -&gt; String.format("long %d", l);
        case Double d  -&gt; String.format("double %f", d);
        case String s  -&gt; String.format("String %s", s);
        default        -&gt; o.toString();
    };
}
</code></pre>
  <p data-lake-id="ub3225fe4" id="ub3225fe4"><br></p>
  <p data-lake-id="ua17504ee" id="ua17504ee"><span data-lake-id="ufb96a77c" id="ufb96a77c">可以看到，以上的switch处理的是一个Object类型，而且case中也不再是精确的值匹配，而是模式匹配了。</span></p>
  <p data-lake-id="ua237e412" id="ua237e412"><br></p>
  <h2 data-lake-id="KB1Dm" id="KB1Dm"><span data-lake-id="u5ca6f0ed" id="u5ca6f0ed">虚拟线程</span></h2>
  <p data-lake-id="ubb18b2f9" id="ubb18b2f9"><br></p>
 </body>
</html>